package bootstrap

import (
	"context"
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"go.uber.org/zap"
	"net/http"
	"os"
	"os/signal"
	"shopping/config"
	"shopping/controller"
	"shopping/dao/mysql"
	"shopping/logger"
	"shopping/utils/middleware"
	"shopping/utils/snowflake"
	"syscall"
	"time"
)

// Run 运行项目
func Run() {
	// 初始化资源
	initBeforeRun()
	// 启动服务
	srv := runGinServer()
	// 监听关闭服务
	shutdown(srv)
}

// initBeforeRun 初始化各种资源
func initBeforeRun() {
	// 加载配置文件
	initConfig()
	// 初始化日志
	initLogger()
	// 初始化MYSQL
	initMySQL()
	// 初始化雪花算法
	initSnowflake()
}

// initConfig 加载配置文件
func initConfig() {
	// 配置文件路径
	var cfn string
	// 用来解析命令行中的命令, 可以在命令行中执行配置, 命令行中没有指定配置文件路径, 使用项目中的配置文件
	// 无论是直接运行, 还是生成可执行文件运行, 都可以在命令后面追加 -conf="./conf/config-dev.yaml" 指定配置文件
	// go run main.go -conf="./conf/config-uat.yaml"
	flag.StringVar(&cfn, "conf", "./conf/config-dev.yaml", "指定配置文件路径")
	// 解析命令行标志
	flag.Parse()
	// 加载配置文件
	err := config.Init(cfn)
	if err != nil {
		panic(err)
	}
}

// initLogger 初始化日志
func initLogger() {
	err := logger.Init(config.Conf.LogConfig)
	if err != nil {
		panic(err)
	}
}

// initMySQL 初始化MySQL
func initMySQL() {
	err := mysql.Init(config.Conf.MySQLConfig)
	if err != nil {
		panic(err)
	}
}

// initSnowflake 初始化雪花算法
func initSnowflake() {
	err := snowflake.Init(config.Conf.StartTime, config.Conf.MachineId)
	if err != nil {
		panic(err)
	}
}

// runGinServer 启动gin服务
func runGinServer() *http.Server {
	// 获取gin引擎
	r := gin.New()
	// 注册中间件
	registerMiddlewares(r)
	// 注册控制器
	controller.RegisterHandlers(r)
	// 注册swagger
	//url := ginSwagger.URL("/swagger/docs.json") // The url pointing to API definition
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
	// 创建服务器
	srv := &http.Server{
		Addr:    fmt.Sprintf("%s:%s", config.Conf.IP, config.Conf.Port),
		Handler: r,
	}
	go func() {
		// 启动HTTP服务器
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Log.Fatal("监听并启动服务失败: \n", zap.Error(err))
		}
	}()
	logger.Log.Info("服务已经启动",
		zap.String("url", fmt.Sprintf("http://localhost:%s", config.Conf.Port)),
		zap.String("swaggerUrl", fmt.Sprintf("http://localhost:%s/swagger/index.html", config.Conf.Port)))
	return srv
}

// shutdown 听信号, 执行关机操作
// param srv 需要关闭的Http Server实例
func shutdown(srv *http.Server) {
	// 定义一个触发退出的信号, 通道的元素是os.Signal, 是操作系统的信号
	quit := make(chan os.Signal)
	// 操作发出syscall.SIGTERM和syscall.SIGINT这两个信号的时候,会把它通知到自定义的quite中
	// kill (没有参数) 默认是 syscall.SIGTERM
	// kill -2 是 syscall.SIGINT
	// kill -9 是 syscall.
	signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
	// 启动之后 这里会阻塞, 等到退出的时候(通道里面有值)才会往下执行
	<-quit
	logger.Log.Info("开始关闭服务...")

	// 创建context 并设置超时时间, 确保服务关闭的操作在指定时间内完成
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(config.Conf.ShutdownOvertime))
	defer cancel()

	// 其他操作, 例如在注册中心中注销服务

	// 调用Http实例的Shutdown方法 关闭服务器
	if err := srv.Shutdown(ctx); err != nil {
		logger.Log.Fatal("服务关闭错误: ", zap.Error(err))
	}
	logger.Log.Info("关闭服务完成...")
}

// registerMiddlewares 注册中间件
func registerMiddlewares(r *gin.Engine) {
	// 打印日志
	r.Use(middleware.GinLogger(logger.Log))
	// 异常保护
	r.Use(middleware.GinRecovery(logger.Log, true))
}
